﻿using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityGameFramework.Editor.AssetBundleTools;

namespace ProjectS.Editor
{
    /// <summary>
    /// 自动加载AssetBundles
    /// </summary>
    public class AssetBundleAuto
    {
        private AssetBundleEditorController m_Controller = null;

        [MenuItem("Project-S/Auto Generate AssetBundles")]
        private static void Open()
        {
            string path = Application.dataPath + "/GameMain/Configs/AssetBundleCollection.xml";
            if (File.Exists(path))
            {
                File.Delete(path);
            }
            AssetDatabase.Refresh();
            AssetBundleAuto auto = new AssetBundleAuto();
            auto.Init();
            auto.GraMath();
        }

        private void Init()
        {
            m_Controller = new AssetBundleEditorController();
            //m_Controller.OnLoadingAssetBundle += OnLoadingAssetBundle;
            //m_Controller.OnLoadingAsset += OnLoadingAsset;
            //m_Controller.OnLoadCompleted += OnLoadCompleted;
            //m_Controller.OnAssetAssigned += OnAssetAssigned;
            //m_Controller.OnAssetUnassigned += OnAssetUnassigned;
            if (m_Controller.Load())
            {
                Debug.Log("Load configuration success.");
            }
            else
            {
                Debug.LogWarning("Load configuration failure.");
            }
        }

        private void AddAssetBundle(string assetBundleName, string assetBundleVariant, bool refresh)
        {
            if (assetBundleVariant == string.Empty)
            {
                assetBundleVariant = null;
            }

            string assetBundleFullName = GetAssetBundleFullName(assetBundleName, assetBundleVariant);
            if (m_Controller.AddAssetBundle(assetBundleName, assetBundleVariant, AssetBundleLoadType.LoadFromFile, false))
            {
                if (refresh)
                {
                    //RefreshAssetBundleTree();
                }

                Debug.Log(string.Format("Add AssetBundle '{0}' success.", assetBundleFullName));
            }
            else
            {
                Debug.LogWarning(string.Format("Add AssetBundle '{0}' failure.", assetBundleFullName));
            }
        }

        private string GetAssetBundleFullName(string assetBundleName, string assetBundleVariant)
        {
            return assetBundleVariant != null ? string.Format("{0}.{1}", assetBundleName, assetBundleVariant) : assetBundleName;
        }

        private void GraMath()
        {
            float Pcur = 0;
            float Psum = 0;
            HashSet<SourceAsset> allSourceAssets = new HashSet<SourceAsset>(m_Controller.GetSourceAssets());
            //1独立ab shader configs DataTables luaScripts
            {
                Pcur = 0;
                Psum = 0;
                EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);
                HashSet<SourceAsset> DelAssets = new HashSet<SourceAsset>();
                Psum = allSourceAssets.Count;
                foreach (SourceAsset asset in allSourceAssets)
                {
                    Pcur++;
                    EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);

                    if (m_Controller.GetAsset(asset.Guid) == null)
                    {
                        string assetSting = asset.FromRootPath;
                        int dotIndex = asset.FromRootPath.IndexOf('.');
                        string assetBundleName = dotIndex > 0 ? asset.FromRootPath.Substring(0, dotIndex) : asset.FromRootPath;
                        string bundleName = assetBundleName;
                        if (assetSting.IndexOf("Temp/") != -1)
                        {
                            if (!DelAssets.Contains(asset))
                            {
                                DelAssets.Add(asset);
                            }
                            continue;
                        }
                        else if (assetSting.IndexOf("Configs/") != -1)
                        {
                            bundleName = "Configs";
                        }
                        else if (assetSting.IndexOf("DataTables/") != -1)
                        {
                            bundleName = "DataTables";
                        }
                        else if (assetSting.IndexOf("LuaScripts/") != -1)
                        {
                            bundleName = "LuaScripts";
                        }
                        else if (assetSting.IndexOf("Shaders/") != -1 || asset.Path.EndsWith(".shader") || asset.Path.EndsWith(".cginc"))
                        {
                            bundleName = "Shaders";
                        }
                        else
                        {
                            continue;
                        }
                        if (!DelAssets.Contains(asset))
                        {
                            DelAssets.Add(asset);
                        }
                        AddAssetBundle(bundleName, null, false);
                        UnityGameFramework.Editor.AssetBundleTools.AssetBundle assetBundle = m_Controller.GetAssetBundle(bundleName, null);
                        if (assetBundle == null)
                        {
                            continue;
                        }
                        m_Controller.AssignAsset(asset.Guid, assetBundle.Name, assetBundle.Variant);
                    }

                }
                foreach (SourceAsset item in DelAssets)
                {
                    if (allSourceAssets.Contains(item))
                    {
                        allSourceAssets.Remove(item);
                    }
                }
                EditorUtility.ClearProgressBar();

            }
            HashSet<string> SinglePaths = new HashSet<string>();
            HashSet<string> CheckPaths = new HashSet<string>();
            //2 进行有向图检测资源
            {
                Psum = 0;
                Pcur = 0;
                EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);

                foreach (SourceAsset item in allSourceAssets)
                {
                    SinglePaths.Add(item.Path);
                    CheckPaths.Add(item.Path);
                }

                List<List<string>> pathss = new List<List<string>>();
                Psum = allSourceAssets.Count;
                Debug.LogError("需要有向图合并的资源" + allSourceAssets.Count);
                //TODO合并资源的算法
                foreach (SourceAsset sourceAsset in allSourceAssets)
                {
                    Pcur++;
                    EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);
                    string path = sourceAsset.Path;
                    List<string> depend_list = new List<string>();
                    string[] files = AssetDatabase.GetDependencies(path, false);

                    if (files.Length > 0)
                    {
                        depend_list.Add(path);
                        if (SinglePaths.Contains(path))
                        {
                            SinglePaths.Remove(path);
                        }
                        for (int i = 0; i < files.Length; i++)
                        {
                            string file = files[i];
                            if (!CheckPaths.Contains(file))
                                continue;
                            if (!file.Equals(path) && !file.EndsWith(".cs") && file.StartsWith("Assets/GameMain"))
                            {
                                depend_list.Add(file);
                                if (SinglePaths.Contains(file))
                                {
                                    SinglePaths.Remove(file);
                                }
                            }
                        }
                        pathss.Add(depend_list);

                    }
                }
                AssetDiGraph graph = new AssetDiGraph(pathss);
                if (new DirectedCycle(graph.GetG()).hasCycle())
                {
                    Debug.LogError("有环");
                }
                else
                {
                    Debug.LogError("无环");
                }

                List<int> listOrder = new DirectedDFS(graph.GetG()).GetReversePost();
                listOrder.Reverse();

                Dictionary<int, List<int>> dict = new Dictionary<int, List<int>>();
                for (int i = 0; i < listOrder.Count; i++)
                {
                    int num = listOrder[i];
                    int result = -1;
                    int sum = graph.GetG().inDegree(num, out result);
                    if (sum > 1)
                    {
                        if (dict.ContainsKey(num))
                        {
                            dict[num].Add(num);
                        }
                        else
                        {
                            List<int> list = new List<int>();
                            list.Add(num);
                            dict.Add(num, list);
                        }
                    }
                    else if (sum == 0)
                    {
                        if (dict.ContainsKey(num))
                        {
                            dict[num].Add(num);
                        }
                        else
                        {
                            List<int> list = new List<int>();
                            list.Add(num);
                            dict.Add(num, list);
                        }
                        //TODO合并
                    }
                    else if (sum == 1)
                    {
                        int parent = getOneInDegreeParent(graph.GetG(), num);
                        if (dict.ContainsKey(parent))
                        {
                            dict[parent].Add(num);
                        }
                    }
                }

                Psum = dict.Count;
                Pcur = 0;
                foreach (var item in dict)
                {
                    Pcur++;
                    EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);
                    string path = graph.name(item.Key);
                    int dotIndex = path.IndexOf('.');
                    int len = "Assets/GameMain/".Length;
                    string bundleName = dotIndex > 0 ? path.Substring(len, dotIndex - len) : path;
                    for (int i = 0; i < item.Value.Count; i++)
                    {
                        if (path.EndsWith(".unity"))
                        {
                            if (i > 0)
                            {
                                if (!bundleName.EndsWith("_Data"))
                                    bundleName = bundleName + "_Data";
                            }
                            else
                            {
                                if (!bundleName.EndsWith("_Scene"))
                                    bundleName = bundleName + "_Scene";
                            }

                        }

                        if (path.EndsWith(".FBX"))
                        {
                            bundleName = bundleName.Replace("@", "");
                        }
                        AddAssetBundle(bundleName, null, false);
                        UnityGameFramework.Editor.AssetBundleTools.AssetBundle assetBundle = m_Controller.GetAssetBundle(bundleName, null);
                        if (assetBundle == null)
                        {
                            continue;
                        }
                        string name = graph.name(item.Value[i]);
                        m_Controller.AssignAsset(AssetDatabase.AssetPathToGUID(name), assetBundle.Name, assetBundle.Variant);
                    }
                }
                EditorUtility.ClearProgressBar();
            }

            //3 无依赖的独立资源进行打包
            {
                Psum = 0;
                Pcur = 0;
                EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);
                Psum = SinglePaths.Count;
                foreach (var path in SinglePaths)
                {
                    Pcur++;
                    EditorUtility.DisplayProgressBar("Save", "Processing...", Pcur / Psum);
                    int index = path.LastIndexOf('/');
                    int len = "Assets/GameMain/".Length;
                    string newPath = path.Substring(len, index - len);
                    string bundleName = newPath;
                    if (path.EndsWith(".unity"))
                    {
                        bundleName += "_Scene";
                    }
                    else
                    {
                        if (path.EndsWith(".FBX"))
                        {
                            bundleName = bundleName.Replace("@", "");
                        }
                        bundleName += "_Data";
                    }
                    AddAssetBundle(bundleName, null, false);
                    UnityGameFramework.Editor.AssetBundleTools.AssetBundle assetBundle = m_Controller.GetAssetBundle(bundleName, null);
                    if (assetBundle == null)
                    {
                        continue;
                    }
                    m_Controller.AssignAsset(AssetDatabase.AssetPathToGUID(path), assetBundle.Name, assetBundle.Variant);
                }
                EditorUtility.ClearProgressBar();
            }

            if (m_Controller.Save())
            {
                Debug.Log("Save configuration success.");
            }
            else
            {
                Debug.LogWarning("Save configuration failure.");
            }
        }

        private int getOneInDegreeParent(DiGraph graph, int num)
        {
            int result = -1;
            int value = -1;
            while (graph.inDegree(num, out result) == 1)
            {
                num = result;
                value = num;
            }
            return value;
        }

        #region delegate

        private void OnLoadingAssetBundle(int index, int count)
        {
            EditorUtility.DisplayProgressBar("Loading AssetBundles", string.Format("Loading AssetBundles, {0}/{1} loaded.", index.ToString(), count.ToString()), (float)index / count);
        }

        private void OnLoadingAsset(int index, int count)
        {
            EditorUtility.DisplayProgressBar("Loading Assets", string.Format("Loading assets, {0}/{1} loaded.", index.ToString(), count.ToString()), (float)index / count);
        }

        private void OnLoadCompleted()
        {
            foreach (var sourceAsset in m_Controller.GetSourceAssets())
            {
                if (m_Controller.GetAsset(sourceAsset.Guid) == null)//根据Guid获取资源
                {
                    string assetSting = sourceAsset.FromRootPath;
                    int dotIndex = sourceAsset.FromRootPath.IndexOf('.');
                    string assetBundleName = dotIndex > 0 ? sourceAsset.FromRootPath.Substring(0, dotIndex) : sourceAsset.FromRootPath;
                    string bundleName = assetBundleName;
                    if (assetSting.IndexOf("Temp/") != -1)
                    {
                        continue;
                    }
                    if (assetSting.IndexOf("Configs/") != -1)
                    {
                        bundleName = "Configs";
                    }
                    else if (assetSting.IndexOf("DataTables/") != -1)
                    {
                        bundleName = "DataTables";
                    }
                    else if (assetSting.IndexOf("LuaScripts/") != -1)
                    {
                        bundleName = "LuaScripts";
                    }
                    else if (assetSting.IndexOf("shaders/") != -1)
                    {
                        bundleName = "shaders";
                    }
                    else if (assetSting.IndexOf("Sounds") != -1)
                    {
                        bundleName = sourceAsset.Folder.FromRootPath;
                    }
                    AddAssetBundle(bundleName, null, false);
                    UnityGameFramework.Editor.AssetBundleTools.AssetBundle assetBundle = m_Controller.GetAssetBundle(bundleName, null);
                    if (assetBundle == null)
                    {
                        continue;
                    }
                    m_Controller.AssignAsset(sourceAsset.Guid, assetBundle.Name, assetBundle.Variant);
                }
            }
            EditorUtility.DisplayProgressBar("Save", "Processing...", 0f);
            if (m_Controller.Save())
            {
                Debug.Log("Save configuration success.");
            }
            else
            {
                Debug.LogWarning("Save configuration failure.");
            }
            EditorUtility.ClearProgressBar();
        }

        private void OnAssetAssigned(SourceAsset[] sourceAssets)
        {

        }

        private void OnAssetUnassigned(SourceAsset[] sourceAssets)
        {

        }

        #endregion delegate
    }

}
